﻿using FlexFlow.Base.Interface;
using FlexFlow.Model;
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace FlexFlow.DataProvider.MongoDB
{
    /// <summary>
    /// 为MongoCollection提供拓展方法
    /// </summary>
    public static class MongoCollectionExtensions
    {
        /// <summary>
        /// 取得当前集合中满足指定筛选条件的第一个实体
        /// </summary>
        /// <typeparam name="T">
        /// 集合中的元素类型
        /// </typeparam>
        /// <param name="col">
        /// 集合实例
        /// </param>
        /// <param name="filter">
        /// 筛选条件
        /// </param>
        /// <returns>
        /// 取得的实例，若数据不存在，则返回null
        /// </returns>
        public static T FirstOrDefault<T>(this IMongoCollection<T> col, FilterDefinition<T> filter)
        {
            FindOptions<T> findOpt = new FindOptions<T>()
            {
                Limit = 1
            };
            Task<IAsyncCursor<T>> findTask = col.FindAsync(filter, findOpt);
            findTask.Wait();
            findTask.Result.MoveNextAsync().Wait();
            if (findTask.Result.Current == null)
            {
                return default(T);
            }
            return findTask.Result.Current.FirstOrDefault();
        }
        /// <summary>
        /// 从当前集合中取得所有满足指定筛选条件的数据
        /// </summary>
        /// <typeparam name="T">
        /// 要取出的数据的类型
        /// </typeparam>
        /// <param name="col">
        /// 集合实例
        /// </param>
        /// <param name="filter">
        /// 筛选条件
        /// </param>
        /// <returns>
        /// 取得的结果集
        /// </returns>
        public static List<T> GetAll<T>(this IMongoCollection<T> col, FilterDefinition<T> filter, FindOptions<T> findOpt = null)
        {
            FindOptions<T> newFindOpt = findOpt;
            if (newFindOpt == null)
            {
                newFindOpt = new FindOptions<T>();
            }
            Task<IAsyncCursor<T>> findTask = col.FindAsync(filter, newFindOpt);
            findTask.Wait();
            findTask.Result.MoveNextAsync().Wait();
            if (findTask.Result.Current == null)
            {
                return new List<T>();
            }
            return findTask.Result.Current.ToArray().ToList();
        }
        /// <summary>
        /// 通过指定名称查询数据(Like模糊查询)
        /// </summary>
        /// <param name="nameLike">
        /// 要查询的实体名称
        /// </param>
        /// <param name="findOpt">
        /// 附加的查找选项
        /// </param>
        /// <param name="otherFilters">
        /// 附加的筛选选项
        /// </param>
        /// <param name="col">
        /// 集合实例
        /// </param>
        /// <returns>
        /// 查询到的结果
        /// </returns>
        public static List<T> QueryByName<T>(this IMongoCollection<T> col, string nameLike, FindOptions<T> findOpt = null, FilterDefinition<T> otherFilters = null) where T : INamedPhysicalModel
        {
            return QueryByField<T>(col, m => m.Name, nameLike, findOpt, otherFilters);
        }
        /// <summary>
        /// 执行查询
        /// </summary>
        /// <typeparam name="T">
        /// 实体类型
        /// </typeparam>
        /// <param name="col">
        /// 实体集合
        /// </param>
        /// <param name="field">
        /// 实体的查询字段
        /// </param>
        /// <param name="val">
        /// 实体查询字段的模糊匹配值
        /// </param>
        /// <param name="findOpt">
        /// 查询选项
        /// </param>
        /// <param name="otherFilters">
        /// 本次查询的附加规则
        /// </param>
        /// <returns>
        /// 查询到的数据集合
        /// </returns>
        public static List<T> QueryByField<T>(this IMongoCollection<T> col, Expression<Func<T, object>> field, string val, FindOptions<T> findOpt = null, FilterDefinition<T> otherFilters = null)
        {
            BsonRegularExpression exp = new BsonRegularExpression(val);
            FilterDefinition<T> filter = Builders<T>.Filter.Regex(field, exp);
            if (otherFilters != null)
            {
                filter = Builders<T>.Filter.And(filter, otherFilters);
            }
            findOpt = findOpt ?? new FindOptions<T>()
            {
                Skip = 0,
                Limit = 10
            };
            return col.GetAll(filter, findOpt);
        }
    }
}
